package com.ithuameng.admin.app.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ithuameng.admin.app.system.dao.SysJobMapper;
import com.ithuameng.admin.app.system.model.SysJob;
import com.ithuameng.admin.app.system.model.admin.dto.SysJobDto;
import com.ithuameng.admin.app.system.service.ISysJobService;
import com.ithuameng.admin.enums.JobStatus;
import com.ithuameng.admin.enums.YesNo;
import com.ithuameng.admin.exception.AdminException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Transactional
@Service
public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements ISysJobService {

	@Resource
	private Scheduler scheduler;

	@Override
	public void init() throws SchedulerException {
		scheduler.clear();
		List<SysJob> jobs = super.list();
		jobs.forEach(this::schedulerAdd);
	}

	@Override
	public IPage<SysJob> search(IPage<SysJob> page, SysJobDto sysJobDto) {
		QueryWrapper<SysJob> queryWrapper = new QueryWrapper<>();
		if (sysJobDto != null) {
			if (StringUtils.isNotBlank(sysJobDto.getJobName())) {
				queryWrapper.like(SysJob.JOB_NAME, sysJobDto.getJobName());
			}
			if (sysJobDto.getStatus() != null) {
				queryWrapper.eq(SysJob.STATUS, sysJobDto.getStatus());
			}
		}
		return super.page(page, queryWrapper);
	}

	@Override
	public List<SysJob> findByJobClassName(String jobClassName) {
		return super.baseMapper.findByJobClassName(jobClassName);
	}

	@Override
	public boolean saveAndScheduleJob(SysJob sysJob) {
		boolean result = this.save(sysJob);
		if (JobStatus.WORKING == sysJob.getStatus()) {
			// 定时器添加
			this.schedulerAdd(sysJob);
		}
		return result;
	}

	@Override
	public boolean run(String id, String parameter) {
		SysJob job = super.getById(id);
		if (job == null) {
			return false;
		}
		Map<String, String> paramMap = new HashMap<>();
		paramMap.put("id", job.getId());
		paramMap.put("name", job.getJobName());
		if (StringUtils.isNotBlank(parameter)) {
			Map<String, String> map = Arrays.stream(parameter.split("&")).map(str -> str.split("="))
					.collect(Collectors.toMap(x -> x[0], x -> x[1], (u, v) -> {
						throw new IllegalStateException(String.format("Duplicate key %s", u));
					}, LinkedHashMap::new));
			paramMap.putAll(map);
		}
		try {
			scheduler.triggerJob(JobKey.jobKey(job.getId()), new JobDataMap(paramMap));
			return true;
		} catch (SchedulerException e) {
			log.error("执行任务失败!", e);
			return false;
		}
	}

	@Override
	public boolean pauseJob(String id) {
		SysJob job = super.getById(id);
		if (job == null) {
			return false;
		}
		try {
			if (scheduler.checkExists(JobKey.jobKey(id))) {
				scheduler.pauseJob(JobKey.jobKey(id));
			}
		} catch (SchedulerException e) {
			log.error("暂停任务失败!", e);
		}
		job.setStatus(JobStatus.STOPED);
		return this.updateById(job);
	}

	@Override
	public boolean resumeJob(String id) {
		SysJob job = super.getById(id);
		if (job == null) {
			return false;
		}
		schedulerDelete(job.getId());
		job.setStatus(JobStatus.WORKING);
		schedulerAdd(job);
		return this.updateById(job);
	}

	@Override
	public boolean editAndScheduleJob(SysJob sysJob) {
		SysJob job = super.getById(sysJob.getId());
		if (job == null) {
			return false;
		}
		if (JobStatus.WORKING == sysJob.getStatus()) {
			schedulerDelete(sysJob.getId());
			schedulerAdd(sysJob);
		} else {
			try {
				scheduler.pauseJob(JobKey.jobKey(sysJob.getId()));
			} catch (SchedulerException e) {
				log.error("暂停任务失败!", e);
			}
		}
		return this.updateById(sysJob);
	}

	@Override
	public boolean deleteAndStopJob(String id) {
		SysJob job = super.getById(id);
		if (job == null) {
			return false;
		}
		schedulerDelete(job.getId());
		return this.removeById(job.getId());
	}

	@Override
	public void deleteAndStopJobBatch(String... ids) {
		if (ids == null || ids.length == 0) {
			return;
		}
		for (int i = 0; i < ids.length; i++) {
			boolean isSuccess = this.deleteAndStopJob(ids[i]);
			if (!isSuccess) {
				log.warn("删除定时任务失败！，jobId={}", ids[i]);
			}
		}
	}

	@Override
	public void startAllJob() throws SchedulerException {
		List<SysJob> jobs = super.list();
		jobs.forEach(j -> {
			try {
				if (!scheduler.checkExists(JobKey.jobKey(j.getId()))) {
					schedulerAdd(j);
				}
				j.setStatus(JobStatus.WORKING);
				this.updateById(j);
			} catch (SchedulerException e) {
				e.printStackTrace();
			}
		});
		scheduler.start();
	}

	@Override
	public void pauseAllJob() throws SchedulerException {
		List<SysJob> jobs = super.list();
		jobs.forEach(j -> {
			try {
				if (scheduler.checkExists(JobKey.jobKey(j.getId()))) {
					scheduler.pauseJob(JobKey.jobKey(j.getId()));
				}
				j.setStatus(JobStatus.STOPED);
				this.updateById(j);
			} catch (SchedulerException e) {
				e.printStackTrace();
			}
		});
		scheduler.standby();
	}

	private void schedulerAdd(SysJob job) {
		try {
			Map<String, String> parameters = null;
			if (YesNo.YES == job.getWithParam()) {
				parameters = Arrays.stream(job.getParameter().split("&")).map(str -> str.split("="))
						.collect(Collectors.toMap(x -> x[0], x -> x[1], (u, v) -> {
							throw new IllegalStateException(String.format("Duplicate key %s", u));
						}, LinkedHashMap::new));
			} else {
				parameters = new HashMap<>();
			}

			parameters.put("id", job.getId());
			parameters.put("name", job.getJobName());

			// 构建job信息
			JobDetail jobDetail = JobBuilder.newJob(getJobClass(job.getJobClassName())).withIdentity(job.getId())
					.usingJobData(new JobDataMap(parameters)).build();

			// 表达式调度构建器(即任务执行的时间)
			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());

			// 按新的cronExpression表达式构建一个新的trigger
			CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(job.getId()).withSchedule(scheduleBuilder)
					.build();
			// 判断是否存在
			if (scheduler.checkExists(JobKey.jobKey(job.getId()))) {
				// 防止创建时存在数据问题 先移除，然后在执行创建操作
				scheduler.deleteJob(JobKey.jobKey(job.getId()));
			}
			scheduler.scheduleJob(jobDetail, trigger);
			// 暂停任务
			if (JobStatus.STOPED == job.getStatus()) {
				scheduler.pauseJob(JobKey.jobKey(job.getId()));
			}
		} catch (SchedulerException e) {
			throw new AdminException("创建定时任务失败", e);
		} catch (RuntimeException e) {
			throw new AdminException(e.getMessage(), e);
		} catch (Exception e) {
			throw new AdminException("后台找不到该类名：" + job.getJobClassName(), e);
		}
	}

	private void schedulerDelete(String jobId) {
		try {
			scheduler.pauseTrigger(TriggerKey.triggerKey(jobId));
			scheduler.unscheduleJob(TriggerKey.triggerKey(jobId));
			scheduler.deleteJob(JobKey.jobKey(jobId));
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new AdminException("删除定时任务失败");
		}
	}

	@SuppressWarnings("unchecked")
	private Class<Job> getJobClass(String classname) throws Exception {
		return (Class<Job>) Class.forName(classname);
	}
}
